{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"../assets/logo.png\" width=\"50\" align=\"left\"> \n",
    "\n",
    "# Range\n",
    "\n",
    "***\n",
    "\n",
    "#### Prerequesites\n",
    "- Sampling Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Imports\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Intro - A Realistic Example\n",
    "\n",
    "<img src=\"../assets/radar_blip.jpg\" width=\"400\">\n",
    "\n",
    "In the movies, radars are commonly used for military applications like detecting an approaching object. The officer watches as a small blip on the screen comes closer and a decision needs to be made. Is it a friend or a foe? What is really happening is the utilization of a radar for ranging, or detecting how far an object is relative to the radar. Range is the first key piece of information that defines what a radar provides. However, there are manys steps that need to be taken to detect this \"object\" in the radars FOV. Here, we will only demonstrate how we can use a radar to obtain a distance of \"something\", which ironically may be nothing at all."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What the Radar Sees\n",
    "\n",
    "In our example, we want to know how far the object is from the radar. Let's say our radar sends out a single chirp which eventually (but quickly) bounces off the plane. We then sample the backscatter of this single chirp as our ADC samples. All the information we need to find the range of this object is encoded in these data samples. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Discovering Meaning in the ADC Samples\n",
    "\n",
    "Since each of the samples are taken at equally spaced time intervals, we have a time log of what happened during the chirp. Every sample is a complex number, meaning we have captured some magnitude of power as well as the phase of the wave at that time. So, our object will theoretically appear as an increase in power in our samples. On the other hand, we can use the distinct phases of each sample to obtain distance.\n",
    "\n",
    "- Power - $P = \\sqrt{I^2+Q^2}$\n",
    "- Phase - $\\angle = \\arctan{(\\frac{I}{Q})}$\n",
    "\n",
    "($I$=Imaginary Component $Q$=Real Component)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Processing Range Information\n",
    "\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1 - Range FFT\n",
    "\n",
    "The first step to obtaining range is by performing an FFT across our ADC samples for a single chirp. This unfortunately does not leave you with a single range. Instead, we obtain multiple \"range bins\". These are exactly what they sound like, bins that store the information for various ranges. \n",
    "\n",
    "The way of obtaining range bins in code is straightforward, we literally just need to take an FFT. We can use a library like numpy to quickly accomplish this task. The data I have provided are samples from a single chirp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Read in chirp data\n",
    "adc_samples = np.loadtxt('../assets/chirp.txt', dtype=np.complex_)\n",
    "\n",
    "# Manually cast to signed ints\n",
    "adc_samples.real = adc_samples.real.astype(np.int16)\n",
    "adc_samples.imag = adc_samples.imag.astype(np.int16)\n",
    "\n",
    "# Take a FFT across ADC samples\n",
    "range_bins = np.fft.fft(adc_samples)\n",
    "\n",
    "# Plot the magnitudes of the range bins\n",
    "plt.plot(np.abs(range_bins))\n",
    "plt.xlabel('Range Bins')\n",
    "plt.ylabel('Reflected Power')\n",
    "plt.title('Interpreting a Single Chirp')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2 - Unit Conversion\n",
    "Great! We have successfully interpreted the data received by the radar. We can clearly see peaks and valleys representing possible objects that our radar may see. This is an important step in using our radar data. We have taken many complex samples that are nonsense to us and used our knowledge to interpret a form of measurment that we can understand. There is already so much we can do with this information.\n",
    "\n",
    "But...there's still a problem you may have noticed, what are the units? My high school physics teacher would say something along the lines of, \"are there 128 cows?\". Well, I didn't lie to you when I said the units are range bins, the bins grouping objects that are within close proximity. Furthermore, the objects at some range bin index are indeed farther than the objects at the previous index and closer than the objects in the next index. That's probably what you didn't want to hear however. You probably wanted to know in terms of some unambiguous unit, say meters from the metric system. To find the units in meters, let's first start off with the following equations...\n",
    "\n",
    "- $f = \\frac{S2 d}{c}$ - The *IF signal* frequency produced by a single object at distance $d$ (where the object appears in the frequency spectrum after the range FFT) \n",
    "    - $f$ - Frequency\n",
    "    - $S$ - Frequency slope of the signal emitted by the chirp\n",
    "    - $d$ - Distance relative to the radar\n",
    "    - $c$ - Speed of light <br> \n",
    "- $\\Delta f > \\frac{1}{T}$ - The minimum separation needed in the frequency spectrum to be resolved by the radar <br>\n",
    "    - $T$ - Sampling period\n",
    "\n",
    "Looking at the first equation and we can see there is a direct relationship between $f$ and $d$...\n",
    "\n",
    "- $f = \\frac{S2 d}{c} \\Rightarrow \\Delta f = \\frac{S2 \\Delta d}{c}$\n",
    "\n",
    "So now we have two separate equations that define $\\Delta f$. Substitution can be now used.\n",
    "\n",
    "- $\\frac{S2 \\Delta d}{c} = \\Delta f \\gt \\frac{1}{T}$\n",
    "- $\\frac{S2 \\Delta d}{c} \\gt \\frac{1}{T}$\n",
    "\n",
    "Finally, we can solve for $\\Delta d$, or the range resolution we can achieve.\n",
    "\n",
    "- $\\Delta d \\gt \\frac{c}{2} \\cdot \\frac{1}{ST}$\n",
    "\n",
    "Since we know $S$ is in some unit of frequency over time, we can simplify $ST$ to just $B$, or the bandwidth of chirp.\n",
    "\n",
    "- $\\Delta d > \\frac{c}{2B}$\n",
    "\n",
    "In other words, the range resolution is only dependent on how large a bandwidth the chirp has. Let's see what information we have to use to try and find this range resolution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Data sampling configuration\n",
    "c = 3e8 # Speed of light (m/s)\n",
    "sample_rate = 2500 # Rate at which the radar samples from ADC (ksps - kilosamples per second)\n",
    "freq_slope = 60 # Frequency slope of the chirp (MHz/us)\n",
    "adc_samples = 128 # Number of samples from a single chirp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Not exactly what we wanted, but the only thing we're missing is our bandwidth $B$. We can still use these parameters to find bandwidth since it is just the span of frequency of the chirp. So, we just need to calculate how much of a frequency span the chirp takes. Ignoring converting units for now, this should be our equation:\n",
    "\n",
    "- $B = S \\cdot \\frac{N}{F_s}$\n",
    "    - $S$ - Frequency slope (frequency/time)\n",
    "    - $N$ - Number of ADC samples (samples)\n",
    "    - $F_s$ - Frequency at which we sample ADC samples (samples / time)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Calculating bandwidth of the chirp, accounting for unit conversion\n",
    "chirp_bandwidth = (freq_slope * 1e12 * adc_samples) / (sample_rate * 1e3)\n",
    "\n",
    "# Using our derived equation for range resolution\n",
    "range_res = c / (2 * chirp_bandwidth)\n",
    "print(f'Range Resolution: {range_res} [meters]')\n",
    "\n",
    "# Apply the range resolution factor to the range indices\n",
    "ranges = np.arange(adc_samples) * range_res\n",
    "powers = np.abs(range_bins)\n",
    "\n",
    "# Now we can plot again with an x-axis that makes sense\n",
    "plt.plot(ranges, powers)\n",
    "plt.xlabel('Range (meters)')\n",
    "plt.ylabel('Reflected Power')\n",
    "plt.title('Interpreting a Single Chirp')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It should be clear at this point that we have a large amount of control over the range resolution. Just with a few tunable parameters we can make our radar have the resolution of a few centimeters or a few meters. On the other hand, we can make our radar have the range a few meters or hundreds of meters."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Extras\n",
    "\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Range Benefits from mmWave Radars\n",
    "\n",
    "Radars can detect objects kilometers away, so why not just use them all the time? In short, they were designed just for that and that isn't always what you want. Futhermore, since the wavelengths of the signals are much larger, there is a much more coarse resolution of the range bins. With mmWave radars, we now have the capability of emitting relatively tiny waves. This gives us the opportunity to range at shorter distances with much greater resolution. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Recap\n",
    "\n",
    "In this section, we learned about how a __radar encodes__ range data within its __ADC samples__. There is a straightforward process to extracting this information that can be easily done in code. In code, we first converted this data into units called __range bins__ by performing an __FFT across multiple ADC samples__. These are the discrete relative distances of objects in the radar's view. Then we found the __range resolution__ of each of these range bins, or the range of distance each bin accounts for. However, in order to find calculate the range resolution of the data we needed to have prior knowledge of the configuration of the __chirp__ sent from the radar and the sampling configuration. After we found this, we used it to convert the range bins to a unit that we are all familiar with."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Looking Forward\n",
    "\n",
    "Range is a good start, but to more clearly understand why radar is widely used in applications like autonomous vehicles, there are a few more basics we would recommend learning about. These include doppler and angle of arrival (AOA), which are within this same basics section.\n",
    "\n",
    "Did this notebook seem like it skipped over things? Well, that's because it did. For example, we never actually __detected__ that object in our example, we just made a plot that showed a bunch of peaks within the range bins. If you're impatient, you can look into our notebook about __CFAR__ and see why there is still more work to do. Also, you may have noticed our plots have a large power peak at range bin zero which is zero meters away. There wasn't anything at zero meters (aside from the radar), this is actually a type of interference from the radar itself. You may want to look into the notebook about __Compensation__ to find out how to solve this problem. There is still much more to learn even about range, keep reading!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***\n",
    "\n",
    "#### Contributors\n",
    "- Dash Kosaka\n",
    "\n",
    "#### Questions, Issues, etc.?\n",
    "Contact by...\n",
    "- email - presenseradar@gmail.com\n",
    "- github - https://github.com/presenseradar/openradar"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
